package com.anlogic.sdk.bsp.settings;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.Logger;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;

import com.anlogic.sdk.bsp.model.IPlatformProject;
import com.anlogic.sdk.tools.IBspTool;
import com.anlogic.sdk.tools.model.DocInfo;
import com.anlogic.sdk.tools.model.DriverInfo;
import com.anlogic.sdk.tools.model.LibraryInfo;
import com.anlogic.sdk.tools.model.OSInfo;
import com.anlogic.sdk.tools.utils.ToolsUtils;
import com.anlogic.sdk.utils.JsonUtils;
import com.anlogic.sdk.utils.LogUtils;
import com.anlogic.sdk.utils.UIUtils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

public class BspSettingTool implements IBspSettingTool {
	private static Logger logger = LogUtils.getLogger(BspSettingTool.class);

	private IPlatformProject platform;

	private IBspTool bspTool;

	private boolean isRestored;

	private String bspResourcePath;

	private String cfgFilePath;

	private String bspProcessor;

	private OSInfo bspOsInfo;

	private Map<String, DocInfo> bspDocInfo = new LinkedHashMap<String, DocInfo>();

	private Map<String, DriverInfo> bspDriverInfo = new LinkedHashMap<String, DriverInfo>();

	private Map<String, LibraryInfo> bspLibInfo = new LinkedHashMap<String, LibraryInfo>();

	private Map<String, Map<String, IProperty>> bspOsPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	private Map<String, Map<String, IProperty>> bspLibPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	private Map<String, Map<String, IProperty>> bspPlatformPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	private Map<String, Map<String, OSInfo>> osInfo = new LinkedHashMap<String, Map<String, OSInfo>>();

	private Map<String, Map<String, DriverInfo>> driverInfo = new LinkedHashMap<String, Map<String, DriverInfo>>();

	private Map<String, List<String>> driverAppName = new LinkedHashMap<String, List<String>>();

	private Map<String, Map<String, LibraryInfo>> libInfo = new LinkedHashMap<String, Map<String, LibraryInfo>>();

	private Map<String, Map<String, String>> propFileMap = new LinkedHashMap<String, Map<String, String>>();

	private Map<String, Map<String, IProperty>> osPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	private Map<String, Map<String, IProperty>> libPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	private Map<String, Map<String, IProperty>> platformPropMap = new LinkedHashMap<String, Map<String, IProperty>>();

	public BspSettingTool(IPlatformProject platform) {
		this.platform = platform;
		this.bspTool = ToolsUtils.getBspTool();
		this.bspTool.setChip(this.platform.getChip());
		this.bspResourcePath = ToolsUtils.getBspResourcePath(this.platform.getChip());
		this.cfgFilePath = ToolsUtils.getConfigFilePath(this.platform.getChip());
		restore();
	}

	public void restore() {
		if (!isRestored) {
			restoreMss();
			restoreConfigs();
		}
		isRestored = true;
		logger.info("Restore mss and config successed.");
	}

	public void restoreMss() {
		logger.info("Restore mssfile...");
		
		this.bspDocInfo.clear();
		this.bspDriverInfo.clear();
		this.bspLibInfo.clear();

		JsonObject mssContent = JsonUtils.readJsonFile(new File(this.platform.getMssPath()));

		this.bspProcessor = mssContent.get("processor").toString();
		
		for (Map.Entry<String, JsonElement> entry : mssContent.get("doc").getAsJsonObject().entrySet()) {
			String key = entry.getKey();
			DocInfo value = JsonUtils.parse(entry.getValue().toString(), DocInfo.class);
			this.bspDocInfo.put(key, value);
		}

		this.bspOsInfo = JsonUtils.parse(mssContent.get("os").toString(), OSInfo.class);
		
		for (Map.Entry<String, JsonElement> entry : mssContent.get("driver").getAsJsonObject().entrySet()) {
			String key = entry.getKey();
			DriverInfo value = JsonUtils.parse(entry.getValue().toString(), DriverInfo.class);
			this.bspDriverInfo.put(key, value);
		}
		
		for (Map.Entry<String, JsonElement> entry : mssContent.get("library").getAsJsonObject().entrySet()) {
			String key = entry.getKey();
			LibraryInfo value = JsonUtils.parse(entry.getValue().toString(), LibraryInfo.class);
			this.bspLibInfo.put(key, value);
		}
		logger.info("Restore mssfile done");
	}

	public void restoreConfigs() {
		logger.info("Restore config file...");
		
		this.osInfo.clear();

		JsonObject cfgContent = JsonUtils.readJsonFile(new File(this.cfgFilePath));

		restoreOs(cfgContent.get("os").getAsJsonObject());
		restoreDrivers(cfgContent.get("driver").getAsJsonObject());
		restoreLibraries(cfgContent.get("library").getAsJsonObject());
		restorePlatform(cfgContent.get("builder").getAsJsonObject());
		if (cfgContent.has("ps_driver_app")) {
			restoreDriverAppNames(cfgContent.get("ps_driver_app").getAsJsonObject());
		}
		if (cfgContent.has("pl_driver_app")) {
			restoreDriverAppNames(cfgContent.get("pl_driver_app").getAsJsonObject());
		}
		logger.info("Restore config file done");
	}

	public void restoreOs(JsonObject jsonObject) {
		logger.debug("Restore os information...");
		
		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			Map<String, OSInfo> map = new LinkedHashMap<String, OSInfo>();
			for (Map.Entry<String, JsonElement> item : entry.getValue().getAsJsonObject().entrySet()) {
				OSInfo value = JsonUtils.parse(item.getValue().toString(), OSInfo.class);
				map.put(item.getKey(), value);
			}
			this.osInfo.put(entry.getKey(), map);
		}
		restoreProperties(jsonObject, Constants.COMPONET_TYPE_OS);
		logger.debug("Restore os information done");
	}

	public void restoreDrivers(JsonObject jsonObject) {
		logger.debug("Restore driver information...");

		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			Map<String, DriverInfo> map = new LinkedHashMap<String, DriverInfo>();
			for (Map.Entry<String, JsonElement> item : entry.getValue().getAsJsonObject().entrySet()) {
				DriverInfo value = JsonUtils.parse(item.getValue().toString(), DriverInfo.class);
				map.put(item.getKey(), value);
			}
			this.driverInfo.put(entry.getKey(), map);
		}
		logger.debug("Restore driver information done");
	}

	public void restoreLibraries(JsonObject jsonObject) {
		logger.debug("Restore library information...");

		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			Map<String, LibraryInfo> map = new LinkedHashMap<String, LibraryInfo>();
			for (Map.Entry<String, JsonElement> item : entry.getValue().getAsJsonObject().entrySet()) {
				LibraryInfo value = JsonUtils.parse(item.getValue().toString(), LibraryInfo.class);
				map.put(item.getKey(), value);
			}
			this.libInfo.put(entry.getKey(), map);
		}
		restoreProperties(jsonObject, Constants.COMPONET_TYPE_LIBRARY);
		logger.debug("Restore library information done");
	}

	public void restorePlatform(JsonObject jsonObject) {
		logger.debug("Restore platform project information...");

		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			JsonObject obj = entry.getValue().getAsJsonObject();
			String propFile = obj.get("property").getAsString();
			String config = obj.get("config").getAsString();
			String destination = obj.get("destination").getAsString();
			String destPropFile = this.platform.getLoc() + "/.settings/" + (new File(propFile)).getName();

			Map<String, String> propFiles = new LinkedHashMap<String, String>();
			propFiles.put("propFile", propFile);
			propFiles.put("config", config);
			propFiles.put("destination", destination);
			propFiles.put("destPropFile", destPropFile);
			this.propFileMap.put(entry.getKey(), propFiles);

			String realPropFile = destPropFile;
			if (!(new File(destPropFile)).exists()) {
				realPropFile = this.bspResourcePath + "/" + propFile;
			}
			this.platformPropMap.put(entry.getKey(), getProperty(realPropFile));
		}
		logger.debug("Restore platform project information done");
	}

	public String getNewest(JsonObject jsonObject) {
		float dest_version = 0.0f;
		String dest_name = "";
		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			String name = entry.getKey();
			float version = Float.parseFloat(name);
			if (version > dest_version) {
				dest_version = version;
				dest_name = name;
			}
		}
		return dest_name;
	}

	public void restoreDriverAppNames(JsonObject jsonObject) {
		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			String driverName = entry.getKey();

			JsonObject obj = entry.getValue().getAsJsonObject();
			JsonObject app = obj.get(getNewest(obj)).getAsJsonObject();

			List<String> appNames = new ArrayList<String>();
			for (Map.Entry<String, JsonElement> item : app.entrySet()) {
				appNames.add(item.getKey());
			}

			this.driverAppName.put(driverName, appNames);
		}
	}

	public void restoreProperties(JsonObject jsonObject, String compType) {
		for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
			JsonObject obj = entry.getValue().getAsJsonObject();
			String version = "0.0";
			for (String item : obj.keySet()) {
				if (Float.parseFloat(item) > Float.parseFloat(version))
					version = item;
			}
			JsonObject newestObj = obj.get(version).getAsJsonObject();
			if (newestObj.has("settings")) {
				String propFile = newestObj.get("settings").getAsJsonObject().get("property").getAsString();
				String config = newestObj.get("settings").getAsJsonObject().get("config").getAsString();
				String destination = newestObj.get("settings").getAsJsonObject().get("destination").getAsString();
				String destPropFile = this.platform.getLoc() + "/.settings/" + (new File(propFile)).getName();

				String keyName = entry.getKey();
				if (compType == Constants.COMPONET_TYPE_OS) {
					keyName = keyName.split("OS_")[1].toLowerCase();
				}
				Map<String, String> propFiles = new LinkedHashMap<String, String>();
				propFiles.put("propFile", propFile);
				propFiles.put("config", config);
				propFiles.put("destination", destination);
				propFiles.put("destPropFile", destPropFile);
				this.propFileMap.put(keyName, propFiles);

				String realPropFile = destPropFile;
				if (!(new File(destPropFile)).exists()) {
					realPropFile = this.bspResourcePath + "/" + propFile;
				}

				if (compType == Constants.COMPONET_TYPE_OS) {
					this.osPropMap.put(keyName, getProperty(realPropFile));
				} else if (compType == Constants.COMPONET_TYPE_LIBRARY) {
					this.libPropMap.put(keyName, getProperty(realPropFile));
				}
			}
		}
	}

	public Map<String, IProperty> getProperty(String propFile) {
		Map<String, IProperty> propMap = new LinkedHashMap<String, IProperty>();

		JsonObject propContent = JsonUtils.readJsonFile(new File(propFile));

		IProperty rootprop = new Property("root");
		rootprop.setCategory(true);

		List<String> supportedProc = new ArrayList<String>();
		List<String> supportedOS = new ArrayList<String>();

		for (Map.Entry<String, JsonElement> entry : propContent.entrySet()) {
			supportedProc.clear();
			supportedOS.clear();
			
			JsonObject propObj = entry.getValue().getAsJsonObject();
			
			if (propObj.has("supportedProc")) {
				for (JsonElement value : propObj.get("supportedProc").getAsJsonArray()) {
					supportedProc.add(value.getAsString());
				}
			}
			
			if (propObj.has("supportedOS")) {
				for (JsonElement value : propObj.get("supportedOS").getAsJsonArray()) {
					supportedProc.add(value.getAsString());
				}
			}
			
			if (!supportedProc.isEmpty() && !supportedProc.contains(platform.getProcessor())) {
				continue;
			}
			
			if (!supportedOS.isEmpty() && !supportedOS.contains(platform.getOs())) {
				continue;
			}
			
			IProperty prop = new Property(entry.getKey());
			prop.setName(propObj.get("Name").getAsString());
			prop.setValue(propObj.get("Value").getAsString());
			prop.setDefaultValue(propObj.get("Default").getAsString());
			prop.setTypeFromString(propObj.get("Type").getAsString());
			prop.setDesc(propObj.get("Description").getAsString());
			prop.setPermit(propObj.get("Permit").getAsBoolean());
			prop.setSupportedProc(supportedProc.toArray(new String[0]));
			prop.setSupportedOS(supportedOS.toArray(new String[0]));

			String propType = propObj.get("Type").getAsString();
			if (propType.equalsIgnoreCase(IProperty.TYPE_BOOLEAN_NAME)) {
				String[] valueOptions = new String[2];
				valueOptions[0] = "true";
				valueOptions[1] = "false";
				prop.setValueOptions(valueOptions);
			} else if (propType.equalsIgnoreCase(IProperty.TYPE_ARRAY_NAME)) {
				List<String> valueOptions = new ArrayList<String>();
				for (JsonElement value : propObj.get("Values").getAsJsonArray()) {
					valueOptions.add(value.getAsString());
				}
				prop.setValueOptions(valueOptions.toArray(new String[0]));
			}

			if (prop.getValueOptions() == null) {
				String[] valueOptions = new String[1];
				valueOptions[0] = prop.getValue();
				prop.setValueOptions(valueOptions);
			}

			prop.setIsEditable(true);

			if (propObj.has("sub_options")) {
				for (Map.Entry<String, JsonElement> item : propObj.get("sub_options").getAsJsonObject().entrySet()) {
					supportedProc.clear();
					supportedOS.clear();
					
					JsonObject subPropObj = item.getValue().getAsJsonObject();
					
					for (JsonElement value : subPropObj.get("supportedProc").getAsJsonArray()) {
						supportedProc.add(value.getAsString());
					}
					if (!supportedProc.contains(platform.getProcessor()))
						continue;
					
					if (subPropObj.has("supportedProc")) {
						for (JsonElement value : subPropObj.get("supportedProc").getAsJsonArray()) {
							supportedProc.add(value.getAsString());
						}
					}
					
					if (subPropObj.has("supportedOS")) {
						for (JsonElement value : subPropObj.get("supportedOS").getAsJsonArray()) {
							supportedProc.add(value.getAsString());
						}
					}
					
					if (!supportedProc.isEmpty() && !supportedProc.contains(platform.getProcessor())) {
						continue;
					}
					
					if (!supportedOS.isEmpty() && !supportedOS.contains(platform.getOs())) {
						continue;
					}

					IProperty subProp = new Property(item.getKey());
					subProp.setName(subPropObj.get("Name").getAsString());
					subProp.setValue(subPropObj.get("Value").getAsString());
					subProp.setDefaultValue(subPropObj.get("Default").getAsString());
					subProp.setTypeFromString(subPropObj.get("Type").getAsString());
					subProp.setDesc(subPropObj.get("Description").getAsString());
					subProp.setPermit(subPropObj.get("Permit").getAsBoolean());
					subProp.setSupportedProc(supportedProc.toArray(new String[0]));
					subProp.setSupportedOS(supportedOS.toArray(new String[0]));

					String subPropType = subPropObj.get("Type").getAsString();
					if (subPropType.equalsIgnoreCase(IProperty.TYPE_BOOLEAN_NAME)) {
						String[] valueOptions = new String[2];
						valueOptions[0] = "true";
						valueOptions[1] = "false";
						subProp.setValueOptions(valueOptions);
					} else if (subPropType.equalsIgnoreCase(IProperty.TYPE_ARRAY_NAME)) {
						List<String> valueOptions = new ArrayList<String>();
						for (JsonElement value : subPropObj.get("Values").getAsJsonArray()) {
							valueOptions.add(value.getAsString());
						}
						subProp.setValueOptions(valueOptions.toArray(new String[0]));
					}

					if (subProp.getValueOptions() == null) {
						String[] valueOptions = new String[1];
						valueOptions[0] = subProp.getValue();
						subProp.setValueOptions(valueOptions);
					}

					subProp.setIsEditable(true);
					subProp.setParent(prop);
					prop.setCategory(true);
					prop.addChild(subProp);
				}
			}
			prop.setParent(rootprop);
			rootprop.addChild(prop);
			propMap.put(entry.getKey(), prop);
		}
		return propMap;
	}

	public IPlatformProject getPlatform() {
		return this.platform;
	}

	public Map<String, Map<String, IProperty>> getLibPropMap() {
		return this.libPropMap;
	}

	public OSInfo getBspOsInfo() {
		return this.bspOsInfo;
	}

	public Map<String, DocInfo> getBspDocInfo() {
		return this.bspDocInfo;
	}

	public Map<String, DriverInfo> getBspDriverInfo() {
		return this.bspDriverInfo;
	}

	public Map<String, LibraryInfo> getBspLibInfo() {
		return this.bspLibInfo;
	}

	public Map<String, Map<String, OSInfo>> getOsInfo() {
		return this.osInfo;
	}

	public Map<String, Map<String, DriverInfo>> getDriverInfo() {
		return this.driverInfo;
	}

	public Map<String, Map<String, LibraryInfo>> getLibInfo() {
		return this.libInfo;
	}

	public String getChipFromHpf(String hpf) {
		return this.bspTool.getChipFromHpf(hpf);
	}

	@Override
	public String[] getOsVersions(String osName) {
		String keyName = "OS_" + osName.toUpperCase();
		return this.osInfo.get(keyName).keySet().toArray(new String[0]);
	}

	@Override
	public String[] getDriverAppNames(String driverName, String os, String chip, String proc) {
		List<String> appNames = this.bspTool.getDriverAppNames(driverName, os.toLowerCase(), chip, proc);
		return appNames.toArray(new String[0]);
	}

	@Override
	public String[] getLibraryNames() {
		return this.libInfo.keySet().toArray(new String[0]);
	}

	@Override
	public String[] getLibraryVersions(String libName) {
		return this.libInfo.get(libName).keySet().toArray(new String[0]);
	}

	@Override
	public String[] getLibDescs(String libName) {
		List<String> descList = new ArrayList<String>();
		for (LibraryInfo lib : this.libInfo.get(libName).values()) {
			descList.add(lib.getDesc());
		}
		return descList.toArray(new String[0]);
	}

	@Override
	public Map<String, IProperty> getProperties(String component, String compType) {
		Map<String, IProperty> propMap = new LinkedHashMap<String, IProperty>();

		if (compType == Constants.COMPONET_TYPE_OS) {
			if (this.bspOsPropMap.containsKey(component)) {
				propMap = this.bspOsPropMap.get(component);
			} else if (this.osPropMap.containsKey(component)) {
				propMap = this.osPropMap.get(component);
			}
		} else if (compType == Constants.COMPONET_TYPE_LIBRARY) {
			if (this.bspLibPropMap.containsKey(component)) {
				propMap = this.bspLibPropMap.get(component);
			} else if (this.libPropMap.containsKey(component)) {
				propMap = this.libPropMap.get(component);
			}
		} else if (compType == Constants.COMPONET_TYPE_PLATFORM) {
			if (this.bspPlatformPropMap.containsKey(component)) {
				propMap = this.bspPlatformPropMap.get(component);
			} else if (this.platformPropMap.containsKey(component)) {
				propMap = this.platformPropMap.get(component);
			}
		}

		return propMap;
	}

	@Override
	public void setOsVersion(String ver) {
		this.bspOsInfo.setVersion(ver);
	}

	@Override
	public void addLibrary(String lib, String ver) {
		this.bspLibInfo.put(lib, this.libInfo.get(lib).get(ver));
	}

	@Override
	public void setLibVersion(String lib, String ver) {
		this.bspLibInfo.put(lib, this.libInfo.get(lib).get(ver));
	}

	@Override
	public void removeLibrary(String lib) {
		this.bspLibInfo.remove(lib);
	}

	@Override
	public void addDriver(String driver, String ver) {
		this.bspDriverInfo.put(driver, this.driverInfo.get(driver).get(ver));
	}

	@Override
	public void setDriverVersion(String driver, String ver) {
		this.bspDriverInfo.put(driver, this.driverInfo.get(driver).get(ver));
	}

	@Override
	public void removeDriver(String driver) {
		this.bspDriverInfo.remove(driver);
	}

	@Override
	public boolean isConfigurable(String component) {
		boolean ret = false;
		if (this.osPropMap.containsKey(component) || this.libPropMap.containsKey(component))
			ret = true;
		return ret;
	}

	@Override
	public void addProperty(String component, String compType, Map<String, IProperty> prop) {
		if (compType == Constants.COMPONET_TYPE_OS)
			this.bspOsPropMap.put(component, prop);
		if (compType == Constants.COMPONET_TYPE_LIBRARY)
			this.bspLibPropMap.put(component, prop);
		if (compType == Constants.COMPONET_TYPE_PLATFORM)
			this.bspPlatformPropMap.put(component, prop);
	}

	public void setProperty(String component, String compType, Map<String, IProperty> prop) {
		if (compType == Constants.COMPONET_TYPE_OS)
			this.bspOsPropMap.put(component, prop);
		if (compType == Constants.COMPONET_TYPE_LIBRARY)
			this.bspLibPropMap.put(component, prop);
		if (compType == Constants.COMPONET_TYPE_PLATFORM)
			this.bspPlatformPropMap.put(component, prop);
	}

	@Override
	public void removeProperty(String component, String compType) {
		if (compType == Constants.COMPONET_TYPE_OS)
			this.bspOsPropMap.remove(component);
		if (compType == Constants.COMPONET_TYPE_LIBRARY)
			this.bspLibPropMap.remove(component);
		if (compType == Constants.COMPONET_TYPE_PLATFORM)
			this.bspPlatformPropMap.remove(component);

	}

	@Override
	public void updateBspHpf(String bsp_loc, String hpf) {
		this.bspTool.updateBspHpf(bsp_loc, hpf);
	}

	@Override
	public void updateAppLinkscript(String bspLocation, String appLocation) {
		this.bspTool.updateAppLinkscript(bspLocation, appLocation);
	}

	@Override
	public void updateMss() {
		String osString = JsonUtils.toJson(bspOsInfo);

		String driverString = "";
		for (Map.Entry<String, DriverInfo> entry : this.bspDriverInfo.entrySet()) {
			DriverInfo driver = entry.getValue();
			driverString += String.format("\"%s\":%s,\n", entry.getKey(), JsonUtils.toJson(driver));
		}
		if (!driverString.isEmpty())
			driverString = driverString.substring(0, driverString.length() - 2);

		String libString = "";
		for (Map.Entry<String, LibraryInfo> entry : this.bspLibInfo.entrySet()) {
			LibraryInfo lib = entry.getValue();
			libString += String.format("\"%s\":%s,\n", entry.getKey(), JsonUtils.toJson(lib));
		}
		if (!libString.isEmpty())
			libString = libString.substring(0, libString.length() - 2);

		String mssString = String.format("{\"processor\":%s, \"doc\":%s, \"os\":%s, \"driver\":{%s}, \"library\":{%s}}",
				this.bspProcessor, JsonUtils.toJson(this.bspDocInfo), osString, driverString, libString);

		JsonUtils.write(new File(this.platform.getMssPath()), mssString);
		bspTool.reloadMssfile(this.platform.getMssPath());
	}

	@Override
	public void updateBspSource() {
		this.bspTool.regenerateBsp(this.platform.getMssPath(), this.platform.getLoc());
	}

	@Override
	public void updateProperties() {
		for (Map.Entry<String, Map<String, IProperty>> entry : this.bspOsPropMap.entrySet()) {
			String configFile = this.bspResourcePath + "/" + this.propFileMap.get(entry.getKey()).get("config");
			String destFile = this.platform.getLoc() + "/" + this.propFileMap.get(entry.getKey()).get("destination");
			String destPropFile = this.propFileMap.get(entry.getKey()).get("destPropFile");
			doUpdateProperties(configFile, destFile, entry.getValue());
			savePropertyFile(destPropFile, entry.getValue());
		}
		for (Map.Entry<String, Map<String, IProperty>> entry : this.bspLibPropMap.entrySet()) {
			String configFile = this.bspResourcePath + "/" + this.propFileMap.get(entry.getKey()).get("config");
			String destFile = this.platform.getLoc() + "/" + this.propFileMap.get(entry.getKey()).get("destination");
			String destPropFile = this.propFileMap.get(entry.getKey()).get("destPropFile");
			doUpdateProperties(configFile, destFile, entry.getValue());
			savePropertyFile(destPropFile, entry.getValue());
		}
		for (Map.Entry<String, Map<String, IProperty>> entry : this.bspPlatformPropMap.entrySet()) {
			String configFile = this.bspResourcePath + "/" + this.propFileMap.get(entry.getKey()).get("config");
			String destFile = this.platform.getLoc() + "/" + this.propFileMap.get(entry.getKey()).get("destination");
			String destPropFile = this.propFileMap.get(entry.getKey()).get("destPropFile");
			doUpdateProperties(configFile, destFile, entry.getValue());
			savePropertyFile(destPropFile, entry.getValue());
		}
	}

	public void doUpdateProperties(String configFile, String destFile, Map<String, IProperty> props) {
		List<String> cfgFileLines = loadConfigFile(configFile);
		List<String> newCfgFileLines = new ArrayList<String>();

		for (String line : cfgFileLines) {
			newCfgFileLines.add(replacePropertyLine(line, props));
		}

		writeConfigFile(newCfgFileLines, destFile);
	}

	public String replacePropertyLine(String line, Map<String, IProperty> props) {
		String newLine = line;
		for (IProperty prop : props.values()) {
			if (!prop.isCategory()) {
				if (line.contains(String.format("#{{%s}}", prop.getName()))) {
					newLine = line.replace(String.format("#{{%s}}", prop.getName()), prop.getValue());
					if (prop.getValue().equalsIgnoreCase("true")) {
						newLine = line.replace(String.format("#{{%s}}", prop.getName()), "1");
					} else if (prop.getValue().equalsIgnoreCase("false")) {
						newLine = line.replace(String.format("#{{%s}}", prop.getName()), "0");
					}
					break;
				}
			} else {
				boolean finded = false;
				for (IProperty p : prop.getChildren()) {
					if (line.contains(String.format("#{{%s}}", p.getName()))) {
						newLine = line.replace(String.format("#{{%s}}", p.getName()), p.getValue());
						if (p.getValue().equalsIgnoreCase("true")) {
							newLine = line.replace(String.format("#{{%s}}", p.getName()), "1");
						} else if (p.getValue().equalsIgnoreCase("false")) {
							newLine = line.replace(String.format("#{{%s}}", p.getName()), "0");
						}
						finded = true;
						break;
					}
				}
				if (finded)
					break;
			}
		}
		return newLine;
	}

	public List<String> loadConfigFile(String filePath) {
		List<String> cfgFileLines = new ArrayList<String>();
		File f = new File(filePath);
		try {
			BufferedReader reader = new BufferedReader(new FileReader(f));
			try {
				while (true) {
					String line = reader.readLine();
					if (line == null)
						break;
					cfgFileLines.add(line);
				}
			} finally {
				reader.close();
			}
		} catch (IOException e) {
			logger.error("Unable to load Config file {}", filePath);
		}
		return cfgFileLines;
	}

	public void writeConfigFile(List<String> cfgFileLines, String filePath) {
		File f = new File(filePath);
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(f.getAbsolutePath()));
			try {
				for (String line : cfgFileLines) {
					writer.write(line + "\n");
				}
			} finally {
				writer.close();
			}
		} catch (IOException e) {
			logger.error("Unable to save config file {}", filePath);
		}
	}

	public void savePropertyFile(String destPropFile, Map<String, IProperty> props) {
		String content = "{";
		for (Map.Entry<String, IProperty> entry : props.entrySet()) {
			IProperty prop = entry.getValue();
			String tempStr = String.format(
					"\"%s\":{\"Name\":\"%s\",\"Value\":\"%s\",\"Default\":\"%s\",\"Type\":\"%s\",\"Description\":\"%s\",\"supportedProc\":[%s],\"Permit\":%s",
					entry.getKey(), prop.getName(), prop.getValue(), prop.getDefaultValue(), prop.getTypeAsString(),
					prop.getDesc(), String.join(",", prop.getSupportedProc()), prop.getPermit());
			if (prop.getTypeAsString() == IProperty.TYPE_ARRAY_NAME) {
				tempStr += ",\"Values\":[";
				for (String value : prop.getValueOptions()) {
					tempStr += "\"" + value + "\",";
				}
				tempStr = tempStr.substring(0, tempStr.length() - 1) + "]";
			}
			if (!prop.isCategory()) {
				content = content + tempStr + "},";
				continue;
			}
			tempStr += ",\"sub_options\":{";
			for (IProperty p : prop.getChildren()) {
				tempStr += String.format(
						"\"%s\":{\"Name\":\"%s\",\"Value\":\"%s\",\"Default\":\"%s\",\"Type\":\"%s\",\"Description\":\"%s\",\"supportedProc\":[%s],\"Permit\":%s",
						p.getName(), p.getName(), p.getValue(), p.getDefaultValue(), p.getTypeAsString(), p.getDesc(),
						String.join(",", p.getSupportedProc()), p.getPermit());
				if (p.getTypeAsString() == IProperty.TYPE_ARRAY_NAME) {
					tempStr += ",\"Values\":[";
					for (String value : p.getValueOptions()) {
						tempStr += "\"" + value + "\",";
					}
					tempStr = tempStr.substring(0, tempStr.length() - 1) + "]";
				}
				tempStr += "},";
			}
			tempStr = tempStr.substring(0, tempStr.length() - 1);
			content = content + tempStr + "}},";
		}
		content = content.substring(0, content.length() - 1);
		content += "}";
		JsonUtils.write(new File(destPropFile), content);
	}

	@Override
	public void regenerateBsp() {
		ProgressMonitorDialog progress = new ProgressMonitorDialog(UIUtils.getActiveShell());
		progress.setCancelable(true);
		try {
			progress.run(true, false, new IRunnableWithProgress() {

				@Override
				public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
					monitor.beginTask("Regenerate BSP", IProgressMonitor.UNKNOWN);
					monitor.setTaskName("Regenerate BSP");
					logger.info("Running Regenerate BSP...");
					logger.debug("Regenerate BSP - update mss");
					updateMss();
					platform.refreshLocal(monitor);
					logger.debug("Regenerate BSP - update bsp source");
					updateBspSource();
					logger.debug("Regenerate BSP - update bsp properties");
					updateProperties();
					platform.refreshLocal(monitor);
					logger.info("Regenerate BSP done.");
				}
			});
		} catch (InvocationTargetException | InterruptedException e) {
			logger.error("Regenerate Bsp Failed {}", e.getMessage());
		}
	}
}
